﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace COFE.Data
{
    public class DBOperationProvider : IDBOperationProvider
    {


        public T ReadDatabase<T>(Func<COFEDB2, T> readAction)
        {
            COFEDBShared.ContextLock.EnterReadLock();
            try
            {
                using (var context = COFEDBShared.CreateContext())
                {
                    var retVal = readAction(context);
                    return retVal;
                }
            }
            finally
            {
                COFEDBShared.ContextLock.ExitReadLock();
            }
        }

        public IEnumerable<T> ReadDatabase<T>(Func<COFEDB2, IEnumerable<T>> readAction)
        {
            COFEDBShared.ContextLock.EnterReadLock();
            try
            {
                using (var context = COFEDBShared.CreateContext())
                {
                    var readOutput = readAction(context);
                    foreach (T retVal in readOutput)
                        yield return retVal;
                }
            }
            finally
            {
                COFEDBShared.ContextLock.ExitReadLock();
            }
        }

        public void ReadDatabase(Action<COFEDB2> readAction)
        {
            COFEDBShared.ContextLock.EnterReadLock();
            try
            {
                using (var context = COFEDBShared.CreateContext())
                {
                    readAction(context);
                }
            }
            finally
            {
                COFEDBShared.ContextLock.ExitReadLock();
            }
        }

        public void ReadDatabase(COFEDB2 context, Action<COFEDB2> readAction)
        {
            if (context != null)
            {
                readAction(context);
                return;
            }

            COFEDBShared.ContextLock.EnterReadLock();
            try
            {
                using (var newContext = COFEDBShared.CreateContext())
                    readAction(newContext);
            }
            finally
            {
                COFEDBShared.ContextLock.ExitReadLock();
            }
        }

        public void ReadDatabase<T>(Func<COFEDB2, T> readAction, Action<COFEDB2, T> processAction)
        {
            COFEDBShared.ContextLock.EnterReadLock();
            try
            {
                using (var context = COFEDBShared.CreateContext())
                {
                    T val = readAction(context);
                    processAction(context, val);
                }
            }
            finally
            {
                COFEDBShared.ContextLock.ExitReadLock();
            }
        }

        public V ReadDatabase<T, V>(Func<COFEDB2, T> readAction, Func<COFEDB2, T, V> processAction)
        {
            COFEDBShared.ContextLock.EnterReadLock();
            try
            {
                using (var context = COFEDBShared.CreateContext())
                {
                    T val = readAction(context);
                    return processAction(context, val);
                }
            }
            finally
            {
                COFEDBShared.ContextLock.ExitReadLock();
            }
        }

        private static void checkContextIsNotModified(COFEDB2 context)
        {
#if DEBUG1
            if (context.ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Added).Count() != 0)
                throw new Exception("Entries added not committed.");

            if (context.ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Deleted).Count() != 0)
                throw new Exception("Entries deleted not committed.");

            if (context.ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Modified).Count() != 0)
                throw new Exception("Entries modified not committed.");
#endif
        }

        public void UpdateDatabase(Action<COFEDB2> writeAction)
        {
            COFEDBShared.ContextLock.EnterWriteLock();
            try
            {
                using (var context = COFEDBShared.CreateContext())
                {
                    writeAction(context);
                    checkContextIsNotModified(context);
                }
            }
            finally
            {
                COFEDBShared.ContextLock.ExitWriteLock();
            }
        }

        public T UpdateDatabase<T>(Func<COFEDB2, T> writeAction)
        {
            COFEDBShared.ContextLock.EnterWriteLock();
            try
            {
                using (var context = COFEDBShared.CreateContext())
                {
                    T retVal = writeAction(context);
                    checkContextIsNotModified(context);
                    return retVal;
                }
            }
            finally
            {
                COFEDBShared.ContextLock.ExitWriteLock();
            }
        }

        public void UpdateDatabase<T>(Func<COFEDB2, T> readAction, Action<COFEDB2, T> writeAction)
        {
            COFEDBShared.ContextLock.EnterWriteLock();
            try
            {
                using (var context = COFEDBShared.CreateContext())
                {
                    T value = readAction(context);
                    writeAction(context, value);
                    checkContextIsNotModified(context);
                }

            }
            finally
            {
                COFEDBShared.ContextLock.ExitWriteLock();
            }
        }

        public void UpdateDatabase<T, T1>(Func<COFEDB2, T> readAction, Func<COFEDB2, T, T1> processAction, Action<COFEDB2, T, T1> writeAction)
        {
            COFEDBShared.ContextLock.EnterUpgradeableReadLock();
            try
            {
                using (var context = COFEDBShared.CreateContext())
                {
                    T value = readAction(context);
                    T1 processedValue = processAction(context, value);
                    COFEDBShared.ContextLock.EnterWriteLock();
                    try
                    {
                        writeAction(context, value, processedValue);
                        checkContextIsNotModified(context);
                    }
                    finally
                    {
                        COFEDBShared.ContextLock.ExitWriteLock();
                    }
                }
            }
            finally
            {
                COFEDBShared.ContextLock.ExitUpgradeableReadLock();
            }
        }


     
    }
}
